﻿using NCS.IConnect.Core.Utilities;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Runtime.InteropServices;
using System.Security;
using System.Security.Permissions;
using System.Security.Principal;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Web.Http;
using System.Web.Http.Controllers;
using System.Web.Http.Filters;

namespace NCS.IConnect.Core.WebApi.Authorization
{
    /// <summary>
    /// This custom <see cref="AuthorizationFilterAttribute"/> is used to perform authorization against iConnect.NET built-in web API.
    /// </summary>
    [ComVisible(false)]
    public class BuiltInApiAuthorizeAttribute : AuthorizationFilterAttribute, ISortable
    {
        /// <summary>
        /// Authorization rule to read iConnect.NET data.
        /// </summary>
        public Func<IPrincipal, bool> AuthorizationRuleForRead { get; private set; }

        /// <summary>
        /// Authorization rule to write iConnect.NET data.
        /// </summary>
        public Func<IPrincipal, bool> AuthorizationRuleForWrite { get; private set; }

        /// <summary>
        /// Create a new <see cref="BuiltInApiAuthorizeAttribute"/>.
        /// </summary>
        /// <param name="authorizationRuleForRead">Authorization rule to read iConnect.NET data.</param>
        /// <param name="authorizationRuleForWrite">Authorization rule to write iConnect.NET data.</param>
        public BuiltInApiAuthorizeAttribute(Func<IPrincipal, bool> authorizationRuleForRead, Func<IPrincipal, bool> authorizationRuleForWrite)
        {
            Guard.ArgumentNotNull(authorizationRuleForRead, "authorizationRuleForRead");
            Guard.ArgumentNotNull(authorizationRuleForWrite, "authorizationRuleForWrite");

            this.AuthorizationRuleForRead = authorizationRuleForRead;
            this.AuthorizationRuleForWrite = authorizationRuleForWrite;
        }

        /// <summary>
        /// Create a new <see cref="BuiltInApiAuthorizeAttribute"/>.
        /// </summary>
        public BuiltInApiAuthorizeAttribute()
        {
            this.AuthorizationRuleForRead = this.AuthorizationRuleForWrite = principal => false;
        }
       
        /// <summary>
        ///  Calls when a process requests authorization.
        /// </summary>
        /// <param name="actionContext">The action context, which encapsulates information for using System.Web.Http.Filters.AuthorizationFilterAttribute.</param>
        [EnvironmentPermission(SecurityAction.LinkDemand, Unrestricted = true)]
        public override void OnAuthorization(HttpActionContext actionContext)
        {
            if (!actionContext.ControllerContext.ControllerDescriptor.GetCustomAttributes<BuiltInApiAttribute>().Any())
            {
                return;
            }

            if(actionContext.ActionDescriptor.GetCustomAttributes<AllowAnonymousAttribute>().Any())
            {
                return;
            }

            if (!Thread.CurrentPrincipal.Identity.IsAuthenticated)
            {
                actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.Unauthorized, Errors.E0007);
                return;
            }

            HttpMethod[] readMethods = new HttpMethod[] { HttpMethod.Get, HttpMethod.Head, HttpMethod.Options, HttpMethod.Trace };
            Func<IPrincipal, bool> authorizationRule = readMethods.Contains(actionContext.Request.Method)
                ? this.AuthorizationRuleForRead:this.AuthorizationRuleForWrite;
            if (!authorizationRule(Thread.CurrentPrincipal))
            {
                actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.Unauthorized, Errors.E0008);
            }
        }

        /// <summary>
        /// The order.
        /// </summary>
        public int Order { get; set; }
    }
}